﻿using System;
using System.Collections.Generic;
using System.Linq;
using MSH.Common.Business;
using MSH.Common.Classes;
using MSH.Common.Helpers;
using NHibernate;
using NHibernate.Linq;

namespace MSH.Server.Helpers
{
    /// <summary>
    /// De UserHelper klasse is verantwoordelijk voor taken die samenhanger met een user.
    /// </summary>
    public static class UserHelper
    {
        private static int locationUpdateMaxHistoryCount;
        private static String activationExpireTime;
        private static String activationSMSMessage;
        private static int activationRequestLimit;
        private static int locationUpdateMaxInterval;

        /// <summary>
        /// Constructor van de UserHelper, zet een aantal variabelen vanuit de configuratie
        /// </summary>
        static UserHelper()
        {
            //Config ophalen
            locationUpdateMaxHistoryCount = int.Parse(System.Configuration.ConfigurationManager.AppSettings["locationUpdateMaxHistoryCount"].ToString());
            activationExpireTime = System.Configuration.ConfigurationManager.AppSettings["activationExpireTime"].ToString();
            activationSMSMessage = System.Configuration.ConfigurationManager.AppSettings["activationSMSMessage"];
            activationRequestLimit = int.Parse(System.Configuration.ConfigurationManager.AppSettings["activationRequestLimit"].ToString());
            locationUpdateMaxInterval = int.Parse(System.Configuration.ConfigurationManager.AppSettings["locationUpdateMaxInterval"].ToString());
        }

        /// <summary>
        /// Registreert een nieuwe, niet geactiveerde, gebruiker en verstuurt een activatiecode per SMS.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="password">Wachtwoord</param>
        /// <param name="mobilePhoneNumber">Telefoonnummer</param>
        /// <returns>Boolean die aangeeft of het registreren gelukt is.</returns>
        public static bool RegisterNewUser(String username, String password, String mobilePhoneNumber)
        {
            try
            {
                User user = new User() { Username = username, Password = password, RegisteredTimestamp = DateTime.UtcNow, LastSeenTimestamp = DateTime.UtcNow };
                user.UserProfile = new UserProfile() { MobilePhoneNumber = mobilePhoneNumber, Sex = GenderEnum.Unknown };
                user.UserSettings = new UserSettings() { LocationPrivacy = PrivacyEnum.Friends, ProfilePrivacy = PrivacyEnum.Friends };

                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    session.Save(user);
                    trans.Commit();
                }

                SendActivationCode(user);

            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.RegisterNewUser(): " + e.ToString());
                return false;
            }

            return true;
        }

        /// <summary>
        /// Probeert een gebruiker te activeren a.d.h.v. een opgegeven activatiecode.
        /// </summary>
        /// <param name="username">Gebruikersnaam van de te activeren gebruiker.</param>
        /// <param name="activationCode">Activatiecode</param>
        /// <returns>Boolean die aangeeft of het activeren gelukt is.</returns>
        public static bool ActivateUser(String username, String activationCode)
        {
            User user = Get(username);

            if (user == null)
                return false;

            try
            {
                if (!user.Activated && user.UserActivation != null && user.UserActivation.Code.Equals(activationCode) && user.UserActivation.ExpireTimestamp > DateTime.UtcNow)
                {
    
                    ISession session = NHibernateHelper.GetCurrentSession();
                    user.Activated = true;
                    session.Delete(user.UserActivation);
                    user.UserActivation = null;

                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Save(user);
                        trans.Commit();
                    }
                }
                else
                {
                    //Invalid code
                    return false;
                }
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.ActivateUser(): " + e.ToString());
                return false;
            }

            return true;
        }

        /// <summary>
        /// Controleert of de gebruikersnaam en wachtwoord geldig zijn voor een geactiveerde gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="password">Wachtwoord</param>
        /// <returns>Boolean die aangeeft of de credentials geldig zijn.</returns>
        public static bool ValidateCredentials(String username, String password)
        {

            bool result = false;

            ISession session = NHibernateHelper.GetCurrentSession();

            using (ITransaction trans = session.BeginTransaction())
            {
                var count = session.Query<User>()
                          .Where(u => u.Username == username)
                          .Where(u => u.Password == password)
                          .Where(u => u.Activated == true)
                          .Count();

                result = count == 1;

                trans.Rollback();
            }

            return result;
        }

        /// <summary>
        /// Controleert of de gebruikersnaam en wachtwoord geldig zijn voor een niet geactiveerde gebruiker met een niet verlopen activatiecode.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="password">Wachtwoord</param>
        /// <returns>Boolean die aangeeft of de credentials geldig zijn.</returns>
        public static bool ValidateCredentialsNotActivatedAndExpired(String username, String password)
        {

            bool result = false;

            ISession session = NHibernateHelper.GetCurrentSession();

            using (ITransaction trans = session.BeginTransaction())
            {
                var count1 = session.Query<User>()
                          .Where(u => u.Username == username)
                          .Where(u => u.Password == password)
                          .Where(u => u.Activated == false)
                          .Where(u => u.UserActivation.ExpireTimestamp < DateTime.UtcNow)
                          .Count();

                result = count1 == 1;

                if (!result)
                {

                    var count2 = session.Query<User>()
                               .Where(u => u.Username == username)
                               .Where(u => u.Password == password)
                               .Where(u => u.Activated == false)
                               .Where(u => u.UserActivation == null)
                               .Count();

                    result = count2 == 1;

                }

                trans.Rollback();
            }

            return result;
        }

        /// <summary>
        /// Controleert of de gebruikersnaam en wachtwoord geldig zijn voor een niet geactiveerde gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="password">Wachtwoord</param>
        /// <returns>Boolean die aangeeft of de credentials geldig zijn.</returns>
        public static bool ValidateCredentialsNotActivated(String username, String password)
        {

            bool result = false;

            ISession session = NHibernateHelper.GetCurrentSession();

            using (ITransaction trans = session.BeginTransaction())
            {
                var count = session.Query<User>()                    
                          .Where(u => u.Username == username)
                          .Where(u => u.Password == password)
                          .Where(u => u.Activated == false)
                          .Where(u => u.UserActivation.ExpireTimestamp >= DateTime.UtcNow)
                          .Count();

                result = count == 1;

                trans.Rollback();
            }

            return result;
        }

        /// <summary>
        /// Retourneert het User object behorende bij een gebruikersnaam
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Het User object van de username</returns>
        public static User Get(String username)
        {
            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    User result = session.Query<User>()
                        .Where(u => u.Username == username)
                        .FirstOrDefault();

                    trans.Rollback();

                    return result;
                }
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.Get(): " + e.ToString());
                return null;
            }
        }

        /// <summary>
        /// Vriend ophalen aan de hand van zijn telefoonnummer
        /// </summary>
        /// <param name="username">Telefoon nummer</param>
        /// <returns>User object behorende bij een telefoonnummer</returns>
        public static User GetFriendByPhoneNumber(String phoneNumber)
        {
            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    User result = session.Query<User>()
                        .Where(u => u.UserProfile.MobilePhoneNumber == phoneNumber)
                        .FirstOrDefault();

                    trans.Rollback();

                    return result;
                }
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.GetFriendByPhoneNumber(): " + e.ToString());
                return null;
            }
        }

        /// <summary>
        /// Controleert of een gebruiker bestaat.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Boolean die aangeeft of de gebruiker bestaat.</returns>
        public static bool Exists(String username)
        {
            bool result = false;

            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    int count = session.QueryOver<User>()
                        .Where(u => u.Username == username)
                        .RowCount();

                    result = count > 0;

                    trans.Rollback();
                }

                return result;
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.Exists(): " + e.ToString());
                return true;
            }

        }

        /// <summary>
        /// Verstuurt per SMS een nieuwe activatiecode naar een gebruiker.
        /// </summary>
        /// <param name="user">Gebruikersnaam</param>
        /// <returns>Boolean die aangeeft of het versturen van de activatiecode gelukt is.</returns>
        public static bool SendActivationCode(User user)
        {
            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                //Generate random activation number
                int activationNumber = new Random().Next(10000000, 99999999);

                if (user.UserActivation != null)
                {
                    session.Delete(user.UserActivation);
                }

                //Add activation number to database
                UserActivation ua = new UserActivation();
                ua.Code = activationNumber.ToString();
                ua.SentTimestamp = DateTime.UtcNow;
                Double expireHours = 0;
                Double.TryParse(activationExpireTime, out expireHours);
                if (expireHours <= 0)
                    ua.ExpireTimestamp = DateTime.UtcNow.AddHours(expireHours);
                else
                    ua.ExpireTimestamp = DateTime.UtcNow.AddHours(24);

                user.ActivationRequestCount++;
                user.UserActivation = ua;

                using (ITransaction trans = session.BeginTransaction())
                {
                    session.Save(user);
                    trans.Commit();
                }

                //Send sms
                String message = activationSMSMessage;
                SMSHelper.SendSMS(user.UserProfile.MobilePhoneNumber, string.Format(message, activationNumber));
                return true;
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.SendActivationCode(): " + e.ToString());
                return false;
            }
        }

        /// <summary>
        /// Verstuurt, indien toegestaan, een nieuwe activatiecode naar een gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="mobilePhoneNumber">Telefoonnummer</param>
        /// <returns>Een enum die aangeeft of het gelukt is of niet. Daarnaast is het mogelijk dat er teveel aanvragen geweest zijn.</returns>
        public static ResendActivationResultEnum ResendActivationCode(String username, String mobilePhoneNumber)
        {
            User user = Get(username);

            if (user == null)
                return ResendActivationResultEnum.Failure;

            if (user.ActivationRequestCount >= activationRequestLimit)
                return ResendActivationResultEnum.RetriesExceeded;
            else
            {
                user.UserProfile.MobilePhoneNumber = mobilePhoneNumber;
                return SendActivationCode(user) ? ResendActivationResultEnum.Success : ResendActivationResultEnum.Failure;
            }
        }

        /// <summary>
        /// Slaat een nieuwe locatie van een gebruiker op.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="latitude">Latitude</param>
        /// <param name="longitude">Longitude</param>
        /// <returns>Object die tekstueel de locatie van de gebruiker representeert.</returns>
        public static UserLocation UpdateLocation(String username, double longitude, double latitude)
        {
            User user = Get(username);

            if (user != null)
            {
                try
                {
                    ISession session = NHibernateHelper.GetCurrentSession();

                    IQuery q = session.CreateSQLQuery("SELECT MAX(\"TIMESTAMP\") FROM UserLocation WHERE \"USER\" = :UserID");

                    q.SetInt32("UserID", user.ID);
                    object result = q.UniqueResult();
                    if (result != null && result is DateTime)
                    {
                        DateTime lastLocationUpdate = (DateTime)result;
                        TimeSpan duration = DateTime.UtcNow.Subtract(lastLocationUpdate).Duration();

                        if (duration < new TimeSpan(0, 0, locationUpdateMaxInterval))
                        {
                            LogHelper.LogMessage("UpdateLocation(): Rejected location update from user " + user.Username + ", due to to high update interval.");
                            return null;
                        }
                    }

                    MSH.Common.DAO.UserLocationData locationData = LocationHelper.GetLocationData(latitude, longitude);
                    locationData.LocationUpdatedTime = DateTime.UtcNow;
                    UserLocation userLocation = new UserLocation()
                    {
                        Longitude = longitude,
                        Latitude = latitude,
                        Timestamp = DateTime.UtcNow,
                        AddressLine = locationData.AddressLine,
                        AdminDistrict = locationData.AdminDistrict,
                        CountryRegion = locationData.CountryRegion,
                        Locality = locationData.Locality,
                        PostalCode = locationData.PostalCode
                    };
                    user.LastUserLocation = userLocation;

                    if (user.UserLocations.Count > locationUpdateMaxHistoryCount)
                    {
                        IQuery dq = session.CreateSQLQuery("DELETE FROM UserLocation WHERE \"USER\" = " + user.ID + " ORDER BY ID ROWS 1");
                        dq.ExecuteUpdate();
                    }

                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Save(user);
                        trans.Commit();
                    }

                    return userLocation;
                }
                catch (Exception e)
                {
                    LogHelper.LogMessage("UserHelper.UpdateLocation(): " + e.ToString());
                    return null;
                } 
            }
            else
            {
                return null;
            }
        }

        /// <summary>
        /// Geeft het gebruikersprofiel van een gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam ingelogde gebruiker</param>
        /// <param name="requestingUsername">Username van het profiel dat opgevraagd word</param>
        /// <returns>Een UserProfile object, tenzij de gebruiker hier geen rechten voor heeft</returns>
        public static Common.DAO.UserProfile GetUserProfile(String username, String requestingUsername)
        {
            User user = Get(username);
            Common.DAO.UserProfile up = new Common.DAO.UserProfile();
            
            if (user != null
                &&
                ((user.UserSettings.ProfilePrivacy == PrivacyEnum.Friends && AreFriends(username, requestingUsername, true)) || 
                user.UserSettings.ProfilePrivacy == PrivacyEnum.Public) || username.Equals(requestingUsername)
                )
            {
                up = new Common.DAO.UserProfile();
                up.Address = user.UserProfile.Address;
                up.City = user.UserProfile.City;
                up.DateOfBirth = user.UserProfile.DateOfBirth;
                up.FirstName = user.UserProfile.FirstName;
                up.MobilePhoneNumber = user.UserProfile.MobilePhoneNumber;
                up.Sex = user.UserProfile.Sex;
                up.SurName = user.UserProfile.SurName;
                up.Website = user.UserProfile.Website;
                up.Zipcode = user.UserProfile.Zipcode;
                if (up.SocialNetworkFields == null)
                    up.SocialNetworkFields = new List<MSH.Common.DAO.SocialNetworkData>();

                if (user.UserSocialNetworkProfileData != null)
                {
                    foreach (UserSocialNetworkProfileData data in user.UserSocialNetworkProfileData)
                    {
                        MSH.Common.DAO.SocialNetworkData socialData = new MSH.Common.DAO.SocialNetworkData();
                        socialData.AccountType = data.AccountType.ToString(); //Dit wordt ook een string.
                        socialData.FieldName = data.FieldName;
                        socialData.Value = data.Value;
                        up.SocialNetworkFields.Add(socialData);
                    }
                }
            }
            else
                LogHelper.LogMessage("User with username '{0}' not found or requesting user is not allowed to view this problem. Can't return UserProfile.", username);

            return up;
        }
        
        /// <summary>
        /// Geeft een lijst van vrienden met hun afstanden. Wanneer je geblokkeerd bent door een vriend wordt deze vriend niet weergegeven in je lijst.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Lijst met Friend objecten.</returns>
        public static List<Common.DAO.Friend> GetFriendList(String username)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't return the requested UserProfile.", username);
                return null;
            }
            else
            {
                //Haal lijst op met vrienden waardoor je geblockt bent
                ISession session = NHibernateHelper.GetCurrentSession();
                IList<UserFriend> blockedList = session.Query<UserFriend>()     
                                                   .Where(uf => uf.Friend == user && uf.Blocked == true )
                                                   .ToList<UserFriend>();

                HashSet<String> userBlockedByFriends = new HashSet<string>();
                foreach (UserFriend uf in blockedList)
                    userBlockedByFriends.Add(uf.User.Username);


                IList<UserFriend> friendList = session.Query<UserFriend>()
                                                .Where(uf => uf.Friend == user)
                                                .ToList<UserFriend>();

                HashSet<String> userFriends = new HashSet<string>();
                foreach (UserFriend uf in friendList)
                    userFriends.Add(uf.User.Username);

                List<Common.DAO.Friend> friends = new List<Common.DAO.Friend>();
                foreach (UserFriend userFriend in user.UserFriends)
                {
                    User friend = userFriend.Friend;

                    //Controleer of hij je vriend is, of alleen een open invite.
                    if (!userFriends.Contains(friend.Username))
                        continue;

                    //Controleer of je niet geblockt bent
                    if (userBlockedByFriends.Contains(friend.Username))
                        continue;

                    Common.DAO.Friend friendDao = new Common.DAO.Friend();
                    friendDao.Username = friend.Username;
                    friendDao.HasNotification = FriendHasNotification(user, friend);
                    if (friend.UserSettings.ProfilePrivacy != PrivacyEnum.Private)
                    {
                        friendDao.FirstName = friend.UserProfile.FirstName;
                        friendDao.SurName = friend.UserProfile.SurName;
                    }
                    friendDao.Status = friend.Status;
                    friendDao.LastSeenTimestamp = friend.LastSeenTimestamp;
                    friendDao.Blocked = userFriend.Blocked;
                    friendDao.profilePrivacy = friend.UserSettings.ProfilePrivacy;
          
                    
                    if(friend.LastUserLocation != null && friend.UserSettings.LocationPrivacy != PrivacyEnum.Private)
                    {
                        if (friendDao.Location == null)
                            friendDao.Location = new Common.DAO.UserLocationData();

                        friendDao.Location.Latitude = friend.LastUserLocation.Latitude;
                        friendDao.Location.Longitude = friend.LastUserLocation.Longitude;
                        friendDao.Location.AddressLine = friend.LastUserLocation.AddressLine;
                        friendDao.Location.AdminDistrict = friend.LastUserLocation.AdminDistrict;
                        friendDao.Location.CountryRegion = friend.LastUserLocation.CountryRegion;
                        friendDao.Location.Locality = friend.LastUserLocation.Locality;
                        friendDao.Location.PostalCode = friend.LastUserLocation.PostalCode;
                        friendDao.Location.LocationUpdatedTime = friend.LastUserLocation.Timestamp;

                        if (user.LastUserLocation != null)
                            friendDao.Location.Distance = LocationHelper.CalculateDistance(user.LastUserLocation.Latitude, friendDao.Location.Latitude, user.LastUserLocation.Longitude, friendDao.Location.Longitude);
                    }
                    friends.Add(friendDao);
                }

                return friends;
            }
        }

        /// <summary>
        /// Slaat een nieuw chatbericht op.
        /// </summary>
        /// <param name="fromUsername">Gebruikersnaam van de zender.</param>
        /// <param name="toUsername">Gebruikersnaam van de ontvanger.</param>
        /// <param name="message">Chatbericht</param>
        public static void NewChatMessage(String fromUsername, String toUsername, String message)
        {
            if (!String.IsNullOrEmpty(message))
            {
                DateTime now = DateTime.UtcNow;
                User from = Get(fromUsername);
                User to = Get(toUsername);

                if (from != null && to != null)
                {
                    ISession session = NHibernateHelper.GetCurrentSession();

                    using (ITransaction trans = session.BeginTransaction())
                    {
                        //Als de ontvanger de zender geblokkeerd heeft, wil die geen berichten van hem ontvangen
                        IList<UserFriend> blockedFriends = session.Query<UserFriend>()
                            .Where(uf => (uf.User.ID == to.ID && uf.Friend.ID == from.ID && uf.Blocked == true))
                            .ToList<UserFriend>();

                        if (blockedFriends.Count == 0)
                        {
                            ChatMessage cm = new ChatMessage();
                            cm.FromUser = from;
                            cm.MessageTimestamp = now;
                            cm.Message = message;

                            to.ChatMessages.Add(cm);

                            session.Save(cm);
                            trans.Commit();
                        }
                        else
                            LogHelper.LogMessage("UserHelper.NewChatMessage(): User is blocked, can't send message.");
                    }
                }
                else if (from == null)
                    LogHelper.LogMessage("User with username '{0}' not found. Can't create chatmessage", fromUsername);
                else if (to == null)
                    LogHelper.LogMessage("User with username '{0}' not found. Can't create chatmessage", toUsername);
            }


        }

        /// <summary>
        /// Geeft het totaal aantal ongelezen chatberichten voor een gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam.</param>
        /// <returns>Totaal aantal ongelezen chatberichten.</returns>
        public static int GetChatMessageCount(String username)
        {
          User user = Get(username);

          if (user != null)
          {
              return user.ChatMessages.Count;
          }

          return -1;
        }

        /// <summary>
        /// Geeft een lijst met chatberichten van een gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Lijst met chatberichten.</returns>
        public static List<Common.DAO.ChatMessage> GetChatMessages(String username)
        {
            User user = Get(username);

            if (user != null)
            {
                List<Common.DAO.ChatMessage> chats = new List<Common.DAO.ChatMessage>();
                List<ChatMessage> messagesToDelete = new List<ChatMessage>();
                if (user.ChatMessages != null)
                {
                    int count = 0;
                    foreach (ChatMessage chatm in user.ChatMessages)
                    {
                        if (count == 10)
                            break;
                        Common.DAO.ChatMessage cm = new Common.DAO.ChatMessage();
                        cm.Message = chatm.Message;
                        cm.MessageTimestamp = chatm.MessageTimestamp;
                        cm.FromUsername = chatm.FromUser.Username;
                        chats.Add(cm);
                        messagesToDelete.Add(chatm);
                        count++;
                    }

                    ISession session = NHibernateHelper.GetCurrentSession();

                    foreach (ChatMessage deleteMessage in messagesToDelete)
                    {
                        user.ChatMessages.Remove(deleteMessage);
                    }
     
                    using (ITransaction trans = session.BeginTransaction())
                    {                                
                        session.Save(user);
                        trans.Commit();
                    }

                    return chats;
                }
                else
                {
                    LogHelper.LogMessage("User with username '{0}' has no waiting chatmessages.", username);
                    return null;
                }
            }
            else
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't get next chatmessage", username);
                return null;
            }
        }

        /// <summary>
        /// Controleert of twee gebruikers bevriend zijn en of dat ze elkaar niet geblokkeerd hebben als de boolean gezet is.
        /// </summary>
        /// <param name="username1">Gebruikersnaam 1</param>
        /// <param name="username2">Gebruikersnaam 2</param>
        /// <param name="inclBlocked">Inclusief blocked check</param>
        /// <returns>Een boolean of de gebruikers vrienden zijn</returns>
        public static bool AreFriends(String username1, String username2, bool inclBlocked)
        {
            bool result = false;

            try
            {

                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    IList<UserFriend> friends = session.Query<UserFriend>()
                        .Where(uf => (uf.User.Username == username1 && uf.Friend.Username == username2) || (uf.User.Username == username2 && uf.Friend.Username == username1))
                        .ToList<UserFriend>();

                    if (inclBlocked)
                    {
                        //Controleer of er geen blokkade is
                        foreach (UserFriend uf in friends)
                        {
                            if (uf.Blocked)
                                return false;
                        }
                    }

                    result = friends.Count > 0;

                    trans.Rollback();
                }

                return result;
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.AreFriends(): " + e.ToString());
                return true;
            }
        }

        /// <summary>
        /// Werkt de LastSeenTimestamp bij van een gebruiker.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        public static void UpdateLastSeenTimestamp(String username)
        {
            if (username == null)
                return;

            User user = Get(username);

            if (user != null)
            {
                user.LastSeenTimestamp = DateTime.UtcNow;

                try
                {
                    ISession session = NHibernateHelper.GetCurrentSession();

                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.SaveOrUpdate(user);
                        trans.Commit();
                    }

                }
                catch (Exception e)
                {
                    LogHelper.LogMessage("UserHelper.UpdateLastSeenTimestamp(): " + e);
                    return;
                }
            }

        }

        /// <summary>
        /// Haal alle vrienden en onbekenden uit de buurt op.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="range">Range waarin gezocht wordt.</param>
        /// <returns>Een lijst met PersonNearby object van vrienden en onbekenden binnen de range.</returns>
        public static List<Common.DAO.PersonNearby> GetPeopleNearby(String username, String range)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't return PeopleNearby", username);
                return null;
            }
            else
            {
                List<Common.DAO.PersonNearby> people = new List<Common.DAO.PersonNearby>();

                if (user.LastUserLocation != null)
                {
                    ISession session = NHibernateHelper.GetCurrentSession();

                    Double maxDistance = Double.Parse(range);
                    if (maxDistance != 0)
                        maxDistance /= 1000;
                    else
                        maxDistance = (Double.Parse(System.Configuration.ConfigurationManager.AppSettings["defaultMaxDistanceInMeters"].ToString()) / 1000);

                    IQuery q = session.CreateSQLQuery("select first 50 {US.*} from \"USER\" US left join USERLOCATION UL on (US.LASTUSERLOCATION = UL.ID) "+ 
                                                      "where ACOS(SIN(:lat)*SIN(ul.LATITUDE/180) +COS(:lat)*COS(ul.LATITUDE/180)*COS((ul.LONGITUDE-:lon)/180))*6371 < :distance AND US.ID != " + user.ID + " " +
                                                      "ORDER BY IIF(EXISTS(SELECT ID FROM USERFRIEND WHERE PARENTUSER = " + user.ID + " AND FRIEND = US.ID), 0, 1)" +
                                                      ", ACOS(SIN(:lat)*SIN(ul.LATITUDE/180) +COS(:lat)*COS(ul.LATITUDE/180)*COS((ul.LONGITUDE-:lon)/180))*6371")
                        .AddEntity("US", user.GetType());

                    q.SetDouble("lat", user.LastUserLocation.Latitude/180);
                    q.SetDouble("lon", user.LastUserLocation.Longitude);
                    q.SetDouble("distance", maxDistance);
                    IList<User> usersNearby = q.List<User>();

                    HashSet<String> userFriends = GetListOfFriendUsernames(user.ID);

                    foreach (User us in usersNearby)
                    {
                        bool isFriend = userFriends.Contains(us.Username);
                        if (us.UserSettings.LocationPrivacy == PrivacyEnum.Public || (isFriend == true && us.UserSettings.LocationPrivacy == PrivacyEnum.Friends))
                        {
                            Common.DAO.PersonNearby personDao = new Common.DAO.PersonNearby();

                            personDao.Username = us.Username;
                            personDao.isFriend = isFriend;
                            if (us.UserSettings.ProfilePrivacy == PrivacyEnum.Public || (isFriend == true && us.UserSettings.ProfilePrivacy == PrivacyEnum.Friends))
                            {
                                personDao.FirstName = us.UserProfile.FirstName;
                                personDao.SurName = us.UserProfile.SurName;
                            }
                                personDao.Status = us.Status;
                                personDao.LastSeenTimestamp = us.LastSeenTimestamp;
                                personDao.profilePrivacy = us.UserSettings.ProfilePrivacy;
                            if (us.LastUserLocation != null)
                            {
                                personDao.Latitude = us.LastUserLocation.Latitude;
                                personDao.Longitude = us.LastUserLocation.Longitude;
                                personDao.AddressLine = us.LastUserLocation.AddressLine;
                                personDao.AdminDistrict = us.LastUserLocation.AdminDistrict;
                                personDao.CountryRegion = us.LastUserLocation.CountryRegion;
                                personDao.Locality = us.LastUserLocation.Locality;
                                personDao.PostalCode = us.LastUserLocation.PostalCode;
                                personDao.Distance = LocationHelper.CalculateDistance(user.LastUserLocation.Latitude, us.LastUserLocation.Latitude, user.LastUserLocation.Longitude, us.LastUserLocation.Longitude);
                            }
                            

                            people.Add(personDao);
                        }
                    }
                    return people;
                }
                else
                {
                   LogHelper.LogMessage("User '{0}' has no location, can't get people nearby.", user.Username);
                    return null;
                }
            }

        }

        /// <summary>
        /// Toevoegen van een vriend relatie tussen twee gebruikers
        /// </summary>
        /// <param name="user">Gebruiker</param>
        /// <param name="friend">Vriend</param>
        public static void AddFriendRelation(User user, User friend)
        {
            UserFriend uf = new UserFriend();

            uf.User = user;
            uf.Friend = friend;
            uf.Blocked = false;

            ISession session = NHibernateHelper.GetCurrentSession();

            using (ITransaction trans = session.BeginTransaction())
            {
                session.Save(uf);
                trans.Commit();
            }
        }

        /// <summary>
        /// Blokkeren van een vriend
        /// </summary>
        /// <param name="user">Gebruiker</param>
        /// <param name="friend">Te blokkeren vriend</param>
        public static void BlockFriend(User user, User friend)
        {
            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    IList<UserFriend> friends = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == user.ID && uf.Friend.ID == friend.ID))
                        .ToList<UserFriend>();

                    if (friends.Count == 1)
                    {
                        UserFriend uf = friends[0];
                        uf.Blocked = true;
                        session.Save(uf);
                        trans.Commit();
                    }
                    else
                    {
                        LogHelper.LogMessage("UserHelper.BlockFriend(): No or more relations between user '{0}' and friend '{1}'", user.Username, friend.Username);
                    }
                }
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.BlockFriend():" + e);
            }
        }

        /// <summary>
        /// Deblokkeren van een vriend
        /// </summary>
        /// <param name="user">Gebruiker</param>
        /// <param name="friend">Te deblokkeren vriend</param>
        public static void UnblockFriend(User user, User friend)
        {
            try
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                using (ITransaction trans = session.BeginTransaction())
                {
                    IList<UserFriend> friends = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == user.ID && uf.Friend.ID == friend.ID))
                        .ToList<UserFriend>();

                    if (friends.Count == 1)
                    {
                        UserFriend uf = friends[0];
                        uf.Blocked = false;
                        session.Save(uf);
                        trans.Commit();
                    }
                    else
                    {
                        LogHelper.LogMessage("UserHelper.UnblockFriend(): No or more relations between user '{0}' and friend '{1}'", user.Username, friend.Username);
                    }
                }
            }
            catch (Exception e)
            {
                LogHelper.LogMessage("UserHelper.UnblockFriend():" + e);
            }
        }

        /// <summary>
        ///  Geef een lijst terug met openstaande invitaties.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Lijst met Friend objecten.</returns>
        public static List<Common.DAO.Friend> GetInvitationList(String username)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't get open invitations", username);
                return null;
            }
            else
            {
                //Haal lijst op met vrienden waardoor je geblockt bent
                ISession session = NHibernateHelper.GetCurrentSession();

                IQuery q = session.CreateSQLQuery("select {UF.*} from userfriend UF where not exists (select * from userfriend UF2 where parentuser = UF.friend and friend = UF.parentuser ) and UF.friend = :USERID")
                        .AddEntity("UF", new UserFriend().GetType());

                q.SetInt32("USERID", user.ID);
                    
                IList<UserFriend> invitations = q.List<UserFriend>();

                List<Common.DAO.Friend> friends = new List<Common.DAO.Friend>();
                
                foreach (UserFriend userFriend in invitations)
                {
                    User friend = userFriend.User;

                    Common.DAO.Friend friendDao = new Common.DAO.Friend();
                    friendDao.Username = friend.Username;
                    if (friend.UserSettings != null && friend.UserSettings.ProfilePrivacy != PrivacyEnum.Private)
                    {
                        friendDao.FirstName = friend.UserProfile.FirstName;
                        friendDao.SurName = friend.UserProfile.SurName;
                    }
                    friendDao.Status = friend.Status;
                    friendDao.LastSeenTimestamp = friend.LastSeenTimestamp;
                    friendDao.profilePrivacy = friend.UserSettings.ProfilePrivacy;
                    friends.Add(friendDao);
                }
                return friends;
            }
        }

        /// <summary>
        ///  Accepteer een vriendverzoek
        /// </summary>
        /// <param name="username">Gebruikersnaam van de ingelogde gebruiker</param>
        /// <param name="usernameFriend">Gebruikersnaam van de toe te voegen vriend</param>
        /// <returns>Result enumeratie</returns>
        public static AcceptFriendInvitationResultEnum AcceptFriendInvitation(String username, String usernameFriend)
        {
            User user = Get(username);
            User friend = Get(usernameFriend);
            if (user == null || friend == null)
            {
                LogHelper.LogMessage("User with username '{0}' or '{1}' not found. Can't accept invitation", username, usernameFriend);
                return AcceptFriendInvitationResultEnum.UserNotExists;
            }
            else
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                IList<UserFriend> invite = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == friend.ID && uf.Friend.ID == user.ID))
                        .ToList<UserFriend>();

                if (invite.Count == 0)
                    return AcceptFriendInvitationResultEnum.NoOpenInvitation;

                IList<UserFriend> alreadyAccepted = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == user.ID && uf.Friend.ID == friend.ID))
                        .ToList<UserFriend>();

                if (alreadyAccepted.Count > 0)
                    return AcceptFriendInvitationResultEnum.AlreadyAccepted;
                

                UserFriend userFriend = new UserFriend();
                userFriend.User = user;
                userFriend.Friend = friend;
                userFriend.Blocked = false;

                using (ITransaction trans = session.BeginTransaction())
                {
                    session.Save(userFriend);
                    trans.Commit();
                }

                return AcceptFriendInvitationResultEnum.Succes;

            }
        }

        /// <summary>
        ///  Wijs een vriendverzoek af
        /// </summary>
        /// <param name="username">Gebruikersnaam van de ingelogde gebruiker</param>
        /// <param name="usernameFriend">Gebruikersnaam van de af te wijzen vriend</param>
        /// <returns>Result enumeratie</returns>
        public static DeclineFriendInvitationResultEnum DeclineFriendInvitation(String username, String usernameFriend)
        {
            User user = Get(username);
            User friend = Get(usernameFriend);
            if (user == null || friend == null)
            {
                LogHelper.LogMessage("User with username '{0}' or '{1}' not found. Can't decline invitation", username, usernameFriend);
                return DeclineFriendInvitationResultEnum.UserNotExists;
            }
            else
            {
                ISession session = NHibernateHelper.GetCurrentSession();
                
                IList<UserFriend> invite = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == friend.ID && uf.Friend.ID == user.ID))
                        .ToList<UserFriend>();

                if (invite.Count == 0)
                    return DeclineFriendInvitationResultEnum.NoOpenInvitation;

                IList<UserFriend> alreadyAccepted = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == user.ID && uf.Friend.ID == friend.ID))
                        .ToList<UserFriend>();

                if (alreadyAccepted.Count > 0)
                    return DeclineFriendInvitationResultEnum.AlreadyAccepted;

                using (ITransaction trans = session.BeginTransaction())
                {
                    session.Delete(invite[0]);
                    trans.Commit();
                }

                return DeclineFriendInvitationResultEnum.Succes;

            }
        }

        /// <summary>
        ///  Verwijder een vriend
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="usernameFriend">Gebruikersnaam van de te verwijderen vriend</param>
        /// <returns>Result enumeratie</returns>
        public static RemoveFriendResultEnum RemoveFriend(String username, String usernameFriend)
        {
            User user = Get(username);
            User friend = Get(usernameFriend);
            if (user == null || friend == null)
            {
                LogHelper.LogMessage("User with username '{0}' or '{1}' not found. Can't remove friend", username, usernameFriend);
                return RemoveFriendResultEnum.UserNotExists;
            }
            else
            {
                ISession session = NHibernateHelper.GetCurrentSession();

                //Verwijder ook de notificaties
                IList<UserNotification> friendNotification = session.Query<UserNotification>()
                        .Where(un => (un.User == user && un.Friend == friend) || (un.User == friend && un.Friend == user))
                        .ToList<UserNotification>();

                foreach (UserNotification un in friendNotification)
                    session.Delete(un);

                IList<UserFriend> friendRelations = session.Query<UserFriend>()
                        .Where(uf => (uf.User.ID == user.ID && uf.Friend.ID == friend.ID) || (uf.User.ID == friend.ID && uf.Friend.ID == user.ID))
                        .ToList<UserFriend>();

                if (friendRelations.Count == 2)
                {
                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Delete(friendRelations[0]);
                        session.Delete(friendRelations[1]);
                        trans.Commit();
                    }
                    return RemoveFriendResultEnum.Succes;
                }
                return RemoveFriendResultEnum.UserIsNotAFriend;
            }
        }

        /// <summary>
        ///  Zet de status van een user
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="status">Status van de gebruiker</param>
        public static void SetStatus(String username, String status)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't set status", username);
            }
            else
            {
                if (user.Status == null || !user.Status.Equals(status))
                {
                    user.Status = status;
                    ISession session = NHibernateHelper.GetCurrentSession();
                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Save(user);
                        trans.Commit();
                    }
                }
            }
        }

        /// <summary>
        /// Update de instellingen van een gebuiker
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="locationPrivacy">Privacy instelling voor locatie</param>
        /// <param name="profilePrivacy">Privacy instelling voor profiel</param>
        public static void UpdateSettings(String username, PrivacyEnum locationPrivacy, PrivacyEnum profilePrivacy)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't update settings", username);
            }
            else
            {
                if (user.UserSettings != null)
                {
                    if (user.UserSettings.LocationPrivacy != locationPrivacy || user.UserSettings.ProfilePrivacy != profilePrivacy)
                    {

                        user.UserSettings.LocationPrivacy = locationPrivacy;
                        user.UserSettings.ProfilePrivacy = profilePrivacy;

                        ISession session = NHibernateHelper.GetCurrentSession();
                        using (ITransaction trans = session.BeginTransaction())
                        {
                            session.Save(user);
                            trans.Commit();
                        }
                    }
                }
                else
                {
                    UserSettings us = new UserSettings();
                    us.LocationPrivacy = locationPrivacy;
                    us.ProfilePrivacy = profilePrivacy;

                    user.UserSettings = us;

                    ISession session = NHibernateHelper.GetCurrentSession();
                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Save(user);
                        trans.Commit();
                    }
                }
            }
        }

        /// <summary>
        /// Update het profiel van een gebuiker
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="firstName">Voornaam</param>
        /// <param name="surname">Achternaam</param>
        /// <param name="address">Adres</param>
        /// <param name="zipcode">Postcode</param>
        /// <param name="city">Woonplaats</param>
        /// <param name="sex">Geslacht</param>
        /// <param name="dateOfBirth">Geboortedatum</param>
        /// <param name="website">Website</param>
        public static void UpdateProfile(String username, String firstName, String surname, String address, String zipcode, String city, String sex, String dateOfBirth, String website)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't update settings", username);
            }
            else
            {
                if (user.UserProfile != null)
                {
                    user.UserProfile.Address = address;
                    user.UserProfile.City = city;
                    user.UserProfile.FirstName = firstName;
                
                    if (!string.IsNullOrEmpty(sex))
                    {
                        user.UserProfile.Sex = (GenderEnum)Enum.Parse(typeof(GenderEnum), sex); 
                    }
                    user.UserProfile.SurName = surname;
                    user.UserProfile.Website = website;
                    user.UserProfile.Zipcode = zipcode;
                    if (!string.IsNullOrEmpty(dateOfBirth))
                    {
                        try
                        {
                            user.UserProfile.DateOfBirth = DateTime.Parse(dateOfBirth);
                        }
                        catch (Exception e)
                        {
                            LogHelper.LogMessage("UserHelper.UpdateProfile(): " + e);
                        }
                    }

                    ISession session = NHibernateHelper.GetCurrentSession();
                    using (ITransaction trans = session.BeginTransaction())
                    {
                        session.Save(user);
                        trans.Commit();
                    }
                }
                else
                    LogHelper.LogMessage("UserHelper(): Userprofile is null, should be made at registration.");
            }
        }

        /// <summary>
        /// Haal een lijst op met de namen van al je vrienden
        /// </summary>
        /// <param name="userId">ID van de gebruiker</param>
        /// <returns>HashSet met de namen van alle vrienden</returns>
        public static HashSet<String> GetListOfFriendUsernames(int userId)
        {
            //maak een lijst met vrienden
            String userFriendQuery = "select {UF.*} from USERFRIEND UF " +
                                     "inner join USERFRIEND UF2 on UF2.PARENTUSER = UF.FRIEND " +
                                     string.Format("where UF.PARENTUSER = {0} AND UF2.FRIEND = {0}", userId);

            ISession session = NHibernateHelper.GetCurrentSession();
            IQuery qFriendList = session.CreateSQLQuery(userFriendQuery).AddEntity("UF", typeof(MSH.Common.Business.UserFriend));

            IList<MSH.Common.Business.UserFriend> results = qFriendList.List<MSH.Common.Business.UserFriend>();

            HashSet<String> userFriends = new HashSet<String>();
            foreach (UserFriend uf in results)
                userFriends.Add(uf.Friend.Username);

            return userFriends;
        }

        /// <summary>
        /// Voeg een notificatie toe voor als een vriend binnen een bepaalde range komt.
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <param name="friendUsername">Gebruikersnaam van de vriend</param>
        /// <param name="latitude">Latitude</param>
        /// <param name="longitude">Longitude</param>
        /// <param name="range">Range waarbinnen de vriend moet komen voor een notificatie in km</param>
        /// <returns>Result enumeratie</returns>
        public static AddFriendNotificationResultEnum AddFriendNotification(String username, String friendUsername, double latitude, double longitude, double range)
        {
            User user = Get(username);
            User friend = Get(friendUsername);
            
            if (user == null || friend == null)
            {
                LogHelper.LogMessage("UserHelper.AddFriendNotification(): User or friend does not exists");
                return AddFriendNotificationResultEnum.UserNotExists;
            }

            using (ISession session = NHibernateHelper.GetCurrentSession())
            {
                List<UserNotification> notification = session.Query<UserNotification>().Where(no => no.User.Username == username && no.Friend.Username == friendUsername).ToList<UserNotification>();

                Double maxRange = Double.Parse(System.Configuration.ConfigurationManager.AppSettings["maxNotificationRange"].ToString());

                if (range < 0 || range > maxRange)
                    return AddFriendNotificationResultEnum.RangeToBig;
                
                UserNotification un;

                if (notification.Count == 1)
                {
                    un = notification[0];
                    un.Latitude = latitude;
                    un.Longitude = longitude;
                    un.Range = range;
                }
                else
                {
                    un = new UserNotification()
                    {
                        User = user,
                        Friend = friend,
                        Latitude = latitude,
                        Longitude = longitude,
                        Range = range
                    };
                }

            
                session.Save(un);

                using (ITransaction trans = session.BeginTransaction())
                {
                    trans.Commit();
                }
            }
           
            return AddFriendNotificationResultEnum.Succes;
        }

        /// <summary>
        /// Heeft een bepaalde vriend al een notificatie
        /// </summary>
        /// <param name="user">Gebruikersnaam</param>
        /// <param name="friend">Vriend</param>
        /// <returns>Boolean of de vriend een notificatie heeft</returns>
        private static Boolean FriendHasNotification(User user, User friend)
        {
            ISession session = NHibernateHelper.GetCurrentSession();

            int count = session.Query<UserNotification>()
                        .Where(un => un.User == user && un.Friend == friend).Count();

            if (count > 0)
                return true;
            else
                return false;
        }

        /// <summary>
        /// Geef een lijst terug vriend suggesties
        /// </summary>
        /// <param name="username">Gebruikersnaam</param>
        /// <returns>Lijst met Friend object.</returns>
        public static List<Common.DAO.Friend> GetFriendSuggestions(String username)
        {
            User user = Get(username);
            if (user == null)
            {
                LogHelper.LogMessage("User with username '{0}' not found. Can't get suggestions", username);
                return null;
            }
            else
            {
                using (ISession session = NHibernateHelper.GetCurrentSession())
                {
                    //Haal de facebook vrienden van de gebruiker op
                    List<FacebookFriend> fbFriends = session.Query<FacebookFriend>().Where(fb => fb.User == user).ToList<FacebookFriend>();
                    //Dit komt in de periodieke taak
                    //FacebookHelper.GetFacebookFriends(username);                
                    List<Common.DAO.Friend> friends = new List<Common.DAO.Friend>();

                    //Controleer welke msh gebruiken
                    foreach (FacebookFriend fbId in fbFriends)
                    {
                        UserSocialNetworkAccount friend = session.Query<UserSocialNetworkAccount>().Where(a => a.UniqueAccountId == fbId.FriendFbID && a.AccountType == "Facebook").FirstOrDefault();
                        if (friend != null)
                        {
                            //Kijk of er al een vriend relatie is, zo niet: voeg toe aan suggesties.
                            int count = session.Query<UserFriend>().Where(uf => (uf.User.Username == username && uf.Friend.Username == friend.User.Username) || (uf.User.Username == friend.User.Username && uf.Friend.Username == username)).Count();
                            if (count == 0)
                            {
                                //Voeg toe aan suggesties
                                Common.DAO.Friend friendDao = new Common.DAO.Friend();
                                friendDao.Username = friend.User.Username;
                                if (friend.User.UserSettings != null && friend.User.UserSettings.ProfilePrivacy == PrivacyEnum.Public)
                                {
                                    friendDao.FirstName = friend.User.UserProfile.FirstName;
                                    friendDao.SurName = friend.User.UserProfile.SurName;
                                }
                                friendDao.Status = friend.User.Status;
                                friendDao.LastSeenTimestamp = friend.User.LastSeenTimestamp;
                                friendDao.profilePrivacy = friend.User.UserSettings.ProfilePrivacy;
                                friends.Add(friendDao);
                            }
                        }    
                    }
                    return friends;
                }
                
            }
        }
    }
}
